using DotNetCommon.Extensions;
using DotNetCommon.Logger;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;

namespace DotNetCommon.Expressions
{
    /*
     * 表达式树结构；
     *   1. 根节点一定是 lambda;
     *   2. 每个lambda都可以携带参数列表;
     *   3. lambda表达式可以嵌套
     *        a). 当函数参数为委托时,这个委托被作为lambda直接嵌套进树中
     *        b). 当函数参数为lambda时,这个lambda被包装了一层quote,然后嵌套进树中
     *   4. 每个lambda都发起一个scope, 同时自己又处于上一个scope中, 这和AsyncLocal的很像;
     *   5. 树的节点可以访问父级和祖父级的参数;
     * 
     * 如下:
     * 
     * lambdaRoot(_parameters):
     *      |-> child1
     *      |-> child2
     *      |    |-> quote
     *      |    |    |-> lambda2(_parameters):
     *      |    |    |    |-> child1
     *      |    |    |    |-> child2
     *      |    |    |    |
     *      |    |-> lambda3
     *      |    |    | -> child1
     *      |    |    | -> child2
     * 
     * 
     * 表达式树被简化, 分几中情况:
     *   1. 常规: 一个节点的子节点中, 
     *              同时存在有无引用父或祖父参数的, 此时就要对没有引用祖父参数的子节点进行简化;
     *              都没有引用父或祖父级参数, 延迟简化到父级节点;
     *   2. 当前节点是lambda
     *      除非这个节点是根节点否则不能简化求值, 因为函数接受的是委托, 不是lambda简化求值后的结果;
     *      试想: ()=>Show(()=>true)     void Show(Func<bool> func);
     *          看似可以将 ()=>true 简化, 但其实不行, 因为Show方法接受的是委托, 
     *          而()=>true简化成常量true或Delegate(编译不执行), 都不能描述这个委托, 可以试着重建这个callExpression:
     *              lambda = ()=>true;
     *              Expression.Call(obj, call.Method, lambda.Compile()); //error
     *              Expression.Call(obj, call.Method, lambda.Compile()()); //error
     *           换个思路考虑, 表达式树中描述委托本身使用的就是 lambda, 所以无法再进行简化, 
     *           就像一个 ConstantExpression 我们无法再对他进行简化
     *   3. 当前节点是quote
     *      一般子节点是 lambda
     *      和lambda一样,本身也不能被简化求值, 除非父节点被一起简化
     *      试想场景1: Expression<Func<int,int>> lambda = i => Show(()=>true) + i
     *          其中: int Show(Expression<Func<bool>> expr) { return 2; }
     *          简化结果: lambda = i => 2 + i
     *      
     *      试想场景2: Expression<Func<int,int>> lambda = i => Show(()=>true, i) + i
     *          其中: int Show(Expression<Func<bool>> expr, int i) { return 2; }
     *          这里就没办法简化, 因为 Show 方法接受的是expr不是委托或常量
     *          
     *      
     *   4. 用户指定不要进行简化的节点
     *      用户指定某个节点不能简化时, 就可能引发兄弟节点的简化操作
     */

    /// <summary>
    /// 表达式树节点
    /// </summary>
    [DebuggerDisplay("NodeType={NodeType}: {Expression}")]
    internal class ExpressionNode
    {
        private static readonly ILogger<ExpressionNode> logger = LoggerFactory.CreateLogger<ExpressionNode>();
        /// <summary>
        /// 是否是根节点
        /// </summary>
        internal bool IsRoot { get; set; }
        /// <summary>
        /// 当前节点的表达式(可能为空,比如: Call[静态方法] 的 Object)
        /// </summary>
        internal Expression Expression { get; set; }
        /// <summary>
        /// 表达式类型(可能为空,比如: Call[静态方法] 的 Object)
        /// </summary>
        internal ExpressionType? NodeType => Expression?.NodeType;
        /// <summary>
        /// 子节点
        /// </summary>
        internal List<ExpressionNode> Children { get; set; }
        /// <summary>
        /// 根节点
        /// </summary>
        internal ExpressionNode Root => Parent == null ? this : Parent.Root;
        /// <summary>
        /// 父节点
        /// </summary>
        internal ExpressionNode Parent { get; set; }

        /// <summary>
        /// 当前节点有更新,请求父节点 rebuild
        /// </summary>
        internal bool _updateRequest { get; set; }

        /// <summary>
        /// 当前节点或其子孙节点是否有更新
        /// </summary>
        internal bool NeedUpdate => _updateRequest ? true : Children.Any(i => i.NeedUpdate);

        /// <summary>
        /// 当前节点是 lambda 节点时, 这个是当前节点的参数列表(表达式树中可能存在多层lambda嵌套(没办法: 有的方法接受func或expression))
        /// </summary>
        /// <remarks>注意: null 表示没有(非 lambda 节点), emptyOrHasElements 表示 lambda 节点有或没有参数</remarks>
        internal List<ParameterExpression> _parameters;

        /// <summary>
        /// 获取参数列表
        /// </summary>
        internal List<ParameterExpression> GetParameters(EnumParameterTreeType parameterTreeType = EnumParameterTreeType.NearestParent)
        {
            switch (parameterTreeType)
            {
                case EnumParameterTreeType.NearestParent:
                    {
                        var node = this;
                        while (node.Parent != null)
                        {
                            if (node.Parent._parameters != null) return node.Parent._parameters;
                            node = node.Parent;
                        }
                        return new List<ParameterExpression>();
                    }
                case EnumParameterTreeType.RootParent:
                    {
                        var node = this;
                        while (!node.IsRoot)
                        {
                            node = node.Parent;
                        }
                        return node._parameters;
                    }
                case EnumParameterTreeType.AllParent:
                    {
                        var list = new List<ParameterExpression>();
                        var node = this.Parent;
                        while (node != null)
                        {
                            if (node._parameters.IsNotNullOrEmpty()) list.AddRange(node._parameters);
                            node = node.Parent;
                        }
                        return list;
                    }
                case EnumParameterTreeType.AllParentIncludeSelf:
                    {
                        var list = new List<ParameterExpression>();
                        var node = this;
                        while (node != null)
                        {
                            if (node._parameters.IsNotNullOrEmpty()) list.AddRange(node._parameters);
                            node = node.Parent;
                        }
                        return list;
                    }
                case EnumParameterTreeType.AllChild:
                    {
                        if (this.Children.IsNullOrEmpty()) return new List<ParameterExpression>();
                        return this.Children.ToFlat(i => i.Children).SelectMany(i => i._parameters).ToList();
                    }
                default:
                    return new List<ParameterExpression>();
            }
        }
        /// <summary>
        /// 当前节点的完整唯一描述(基于此实现缓存,和具体值无关,只和表达式写法和Type有关)
        /// </summary>
        internal string FullMarkString { get; set; }
        /// <summary>
        /// 当前节点是否是参数(是参数则阻止自身及其父节点被简化并可能引发兄弟节点的简化操作)
        /// </summary>
        internal bool IsParameter { get; set; }
        /// <summary>
        /// 假的参数(用户手动指定保留node后,通过 FakeParameter 阻止父级对其简化, 注意: 需要将 FakeParameter 添加到root的参数列表中)
        /// </summary>
        internal ParameterExpression FakeParameter { get; set; }
        /// <summary>
        /// 自身或子节点是否引用父级或祖父级参数(需要使用前自行计算, 参考: hasParameter(GetParameters(EnumParameterTreeType.AllParent)))
        /// </summary>
        internal bool HasParameter { get; set; }
        /// <summary>
        /// 检查自身或子节点是否引用 parameters 中的参数
        /// </summary>
        /// <param name="parameters"></param>
        /// <returns></returns>
        internal bool hasParameter(List<ParameterExpression> parameters)
        {
            if (parameters.IsNullOrEmpty()) return false;
            if (IsParameter) return parameters.Any(i => i == Expression || i == FakeParameter);
            return Children.IsNotNullOrEmpty() && Children.Any(i => i.hasParameter(parameters));
        }
        /// <summary>
        /// 当前节点是否是局部变量
        /// </summary>
        internal bool IsLocalVariable { get; set; }

        /// <summary>
        /// 表达式编译缓存
        /// </summary>
        private static ConcurrentDictionary<string, Delegate> _caches = new ConcurrentDictionary<string, Delegate>();
        /// <summary>
        /// 存储简化后计算的常量(根节点有一个就足够了)
        /// </summary>
        internal Dictionary<ParameterExpression, object> _outMidValues = null;
        internal Dictionary<ParameterExpression, object> outMidValues => _outMidValues ?? Parent?.outMidValues;

        /// <summary>
        /// 实际的简化方法: 将常量提取成参数,rebuid一个表达式出来,编译后并缓存,调用时将哪些常量值都传进去
        /// </summary>
        internal object Reduce()
        {
            var consts = new List<(object Value, Type Type)>();
            var parameters = new List<ParameterExpression>();
            Visit(this);
            //使用FullMarkString对于那些复杂 Object 是否能区别开? 比如: 常量 dic:{"name","小明"} 和 dic: {"age":18}
            //这种非基础类型的常量不可能直接出现在表达式中,只可能是由子节点简化而来,他们简化后都是被放进 outMidValues 中
            var dele = _caches.GetOrAdd(FullMarkString, key =>
            {
                var lambda = Expression.Lambda(Expression, parameters.ToArray());
                try
                {
                    var dele = lambda.Compile();
                    logger.LogDebug($"Reduce: lambda.Compile,FullMarkString={FullMarkString}");
                    return dele;
                }
                catch (InvalidOperationException ex)
                {
                    //示例: "variable 'i' of type 'System.Int32' referenced from scope '', but it is not defined"
                    if (ex.Message.StartsWith("variable ") && ex.Message.EndsWith(" but it is not defined"))
                    {
                        throw new Exception($"{ex.Message}\r\n可能得原因之一是在不能简化的表达式后加了 Eval() 标记, 如: (string i) => new {{ Detail = new {{ Name = i+\"abc\" }}.Eval() }}", ex);
                    }
                    throw;
                }
            });
            return dele.DynamicInvoke(consts.Select(i => i.Value).ToArray());

            void Visit(ExpressionNode node)
            {
                if (node.Expression == null) return;
                if (node.Children.IsNotNullOrEmpty())
                {
                    //当外层 lambda 简化内层 lambda 的时候,内层 lambda 下的 parameter 没有 children
                    for (int i = 0; i < node.Children.Count; i++)
                    {
                        Visit(node.Children[i]);
                    }
                }
                if (node.NodeType == ExpressionType.Constant)
                {
                    //如果是常量则提取和替换
                    var item = ((node.Expression as ConstantExpression).Value, node.Expression.Type);
                    consts.Add(item);
                    var para = Expression.Parameter(item.Type);
                    node.Expression = para;
                    parameters.Add(para);
                    //不需要 简化过程中肯定会 rebuild ,最终简化后的结果肯定不会再参与简化一次(同一scope内),而且简化后整体变成了 parameter,在外层处理 rebuid
                    //node._updateRequest = true;
                }
                else if (node.NodeType == ExpressionType.Parameter)
                {
                    //内层lamda被简化后,值放到了 outMidValues 中,式中是 parameter
                    var p = node.Expression as ParameterExpression;
                    if (outMidValues.TryGetValue(p, out var val))
                    {
                        var item = (val, p.Type);
                        consts.Add(item);
                        parameters.Add(p);
                    }
                }
                else
                {
                    //非常量则重新组装 Expression
                    var visit = ExpressionHelper.GetVisit(node.NodeType.Value);
                    node.Expression = visit.Rebuild(node);
                }
            }
        }
    }

    /// <summary>
    /// 获取参数列表
    /// </summary>
    internal enum EnumParameterTreeType
    {
        /// <summary>
        /// 最接近自己的父级(不包含自己)
        /// </summary>
        NearestParent,
        /// <summary>
        /// 根节点
        /// </summary>
        RootParent,
        /// <summary>
        /// 自己所有的父级(不包含自己)
        /// </summary>
        AllParent,
        /// <summary>
        /// 自己所有的父级(包含自己)
        /// </summary>
        AllParentIncludeSelf,
        /// <summary>
        /// 所有的子级别
        /// </summary>
        AllChild,
    }
}
